from random import randint
from tkinter import Tk, Frame, Label, Entry, Text, Button, IntVar, Radiobutton, Checkbutton, messagebox


def completion(matrix_half):  # 补全半个矩阵
    length = len(matrix_half)
    for i in range(length):
        for j in range(length):
            if matrix_half[i][j] == 1 and matrix_half[j][i] != 1:
                matrix_half[i][j] = 1 / matrix_half[j][i]
            if i == j:
                matrix_half[i][j] = 1
    return matrix_half


def matrix_transpose(matrix):  # 矩阵转置
    return [[matrix[j][i] for j in range(len(matrix))] for i in range(len(matrix[0]))]


def matrix_multiplication(matrix_2, matrix_1):  # 二维与一维的矩阵乘法
    return [sum(matrix_2[j][i] * matrix_1[i] for i in range(len(matrix_1))) for j in range(len(matrix_2))]


def summation(matrix_b):  # 求和法
    matrix_b = matrix_transpose(matrix_b)
    b_ = [[i for i in j] for j in matrix_b]  # 深度copy 等同于 b = copy.deepcopy(matrix_b)
    matrix_b = [[matrix_b[i][j] / sum(b_[i]) for j in range(len(matrix_b))] for i in range(len(matrix_b[0]))]  # 归一化
    matrix_b = matrix_transpose(matrix_b)
    v = [sum(i) for i in matrix_b]  # 按行求和得到v
    w = [i / sum(v) for i in v]  # 归一化得到w
    return v, w


def rooting(matrix_b):  # 求根法
    def q_root(list0):  # 相乘再开方
        quadrature = 1
        for i in list0:
            quadrature *= i
        root_ = quadrature ** (1 / len(list0))
        return root_

    v = [q_root(i) for i in matrix_b]  # 按行相乘再开方
    w = [i / sum(v) for i in v]  # 归一化得到w
    return v, w


def check(matrix_a, method=1):  # 一致性检验,1求根法，0求和法
    ri = {1: 0, 2: 0, 3: 0.58, 4: 0.92, 5: 1.12, 6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45}  # RI表
    weight = rooting(matrix_a)[1] if method == 1 else summation(matrix_a)[1]
    a_w = matrix_multiplication(matrix_a, weight)
    lambda_max = sum([a_w[i] / weight[i] for i in range(len(weight))]) / len(a_w)
    if len(a_w) != 1:
        ci = (lambda_max - len(a_w)) / (len(a_w) - 1)  # CI值
    else:
        ci = 0
    ri = ri[len(a_w)]  # RI值
    if ri != 0:
        cr = ci / ri  # CR值
    else:
        cr = 0
    best = weight.index(max(weight))
    return weight, a_w, lambda_max, ci, ri, cr, best, True if cr < 0.1 else False


def check_all(matrix_, matrix_s, method=1):  # 层次总排序组合一致性检验
    w_ = check(matrix_)[0]  # 上一层的一致性检验值
    ci = sum([w_[i] * check(matrix_s[i], method)[3] for i in range(len(matrix_s))])
    ri = sum([w_[i] * check(matrix_s[i], method)[4] for i in range(len(matrix_s))])  # 这一层的一致性检验值权重和
    if ri != 0:
        cr = ci / ri
    else:
        cr = 0
    return cr, True if cr < 0.1 else False


def choice_best(target_, programmes_, method=1):
    t = check(target_)[0]
    p = matrix_transpose([check(i)[0] for i in programmes_])
    w = matrix_multiplication(p, t)
    best = w.index(max(w))
    cr, check_ = check_all(target_, programmes_, method)
    return t, p, w, best, cr, check_


root = Tk()
root.title('AHP计算  by：wld')
# 生成表格
frame_first = Frame(root)
frame_first.pack(side='top')
Label(frame_first, text='准则数：', font=('宋体', 15)).pack(side='left')
entry1 = Entry(frame_first, font=("宋体", 15), fg='blue')
entry1.insert(0, '4')
entry1.config(width=3, justify='center')
entry1.pack(side='left')
Label(frame_first, text='方案数：', font=('宋体', 15)).pack(side='left')
entry2 = Entry(frame_first, font=("宋体", 15), fg='blue')
entry2.insert(0, '5')
entry2.config(width=3, justify='center')
entry2.pack(side='left')
check_v = IntVar()
check_b = Checkbutton(frame_first, text='随机初始化', variable=check_v, font=('宋体', 12))
check_b.pack(side='left')
# 整体展示
frame_show = Frame(root)
frame_show.pack(side='top')
all_num = []


def first():
    option = int(entry1.get())  # 准则数
    ways = int(entry2.get())  # 方案数
    round_ = int(entry_small_num.get())  # 保留小数
    if option <= 0 or ways < 0 or option > 9 or ways > 9:
        messagebox.showerror(title='错误！', message='你可动点脑子吧！\n这软件全是bug，别来卡了！\n取值0到9，好好想想！!')
        exit(0)
    if option * (ways + 1) > 36:
        messagebox.showinfo(title='提示！',
                            message='准则和方案太多了！\n你的屏幕太小显示不过来！\n倒是也能算，只是你看不见我也没办法！')
    random_ = check_v.get()
    t_ = ['目标'] + ['目标']
    t_o = ['准则'] + [f'准则{i + 1}' for i in range(option)]
    o_w = ['方案'] + [f'方案{i + 1}' for i in range(ways)]
    f = ['1', '1/2', '2', '1/3', '3', '1/4', '4', '1/5', '5', '1/6', '6', '1/7', '7', '1/8', '8', '1/9', '9', '1/10',
         '10', '1', '1/2', '2', '1/3', '3', '1/4', '4', '1/5', '5', '1/6', '6', '1/7', '7', '1/8', '8', '1/9', '9']

    def table(u, d):
        frame_n_t = Frame(frame_show)
        frame_n_t.pack(side='top')
        for one in range(len(u) - 1):
            frame_one = Frame(frame_n_t)
            frame_one.pack(side='left')
            frame_n = Frame(frame_one)
            frame_n.pack(side='top')
            num = [[Entry(frame_n, width=6, justify='center', font=("宋体", 15)) for _ in range(len(d))] for _ in d]
            for row in range(len(num)):
                for column in range(len(num[0])):
                    if row == 0 and column == 0:
                        num[row][column].insert('end', u[one + 1])
                    elif row == 0 and column != 0:
                        num[row][column].insert('end', d[column])
                    elif row != 0 and column == 0:
                        num[row][column].insert('end', d[row])
                    elif row < column:
                        num[row][column].insert('end', str(f[(randint(0, int(max(ways, option))))]) if random_ else '1')
                    else:
                        num[row][column].insert('end', '1')
                        num[row][column].config(state='disabled', bg='gray')
                    num[row][column].grid(row=row, column=column)

            text = Text(frame_one, font=("宋体", 15), width=6 * (len(d)), height=2 * (len(d)))
            number = [[float(eval(i.get())) for i in j[1:]] for j in num[1:]]
            number = completion(number)
            all_num.append(num)
            tip = ['Weight:', 'A_w:', 'λ_max:', 'CI:', 'RI:', 'CR:', 'Best:', '一致性检验:']
            info = check(number)
            for i in range(8):
                if isinstance(info[i], list):
                    text.insert('end', tip[i] + str([round(j, round_) for j in info[i]]) + '\n')
                else:
                    if tip[i] == 'Best:':
                        text.insert('end', tip[i] + str(d[int(info[i]) + 1]) + '\n')
                    else:
                        text.insert('end', tip[i] + str(info[i]) + '\n')
            if not info[-1]:
                text.config(foreground='red')
            # text.pack()  # 不显示文字框

    table(t_, t_o)
    if len(o_w) > 1:
        table(t_o, o_w)
    button0.config(state='disabled')
    entry1.config(state='disabled')
    entry2.config(state='disabled')
    check_b.config(state='disabled')
    frame_way.pack(side='top')


def solve():
    option = int(entry1.get())
    ways = int(entry2.get())
    round_ = int(entry_small_num.get())
    method = b.get()
    t_ = ['目标'] + ['目标']
    t_o = ['准则'] + [f'准则{i + 1}' for i in range(option)]
    o_w = ['方案'] + [f'方案{i + 1}' for i in range(ways)]
    names = ['目标/准则'] + [f'准则{i + 1}/方案' for i in range(option)]
    root1 = Tk()
    root1.title('运算结果  by：wld')
    frame_show1 = Frame(root1)
    frame_show1.pack(side='top')
    number = [[[eval(i.get()) for i in j[1:]] for j in k[1:]] for k in all_num]
    number = [completion(i) for i in number]
    numbers = []
    s = {0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10', 11: '11', 12: '12',
         13: '13', 14: '14', 15: '15', 16: '16', 17: '17', 18: '18', 19: '19', 20: '20', 21: '21',
         1 / 2: "1/2", 1 / 3: '1/3', 1 / 4: '1/4', 1 / 5: '1/5', 1 / 6: '1/6', 1 / 7: '1/7', 1 / 8: '1/8', 1 / 9: '1/9',
         1 / 10: '1/10', 1 / 11: '1/11', 1 / 12: '1/12', 1 / 13: '1/13', 1 / 14: '1/14', 1 / 15: '1/15', 1 / 16: '1/16',
         1 / 17: '1/17', 1 / 18: '1/18', 1 / 19: '1/19', 1 / 20: '1/20', 1 / 21: '1/21'}

    def table1(u, d, h=0):
        frame_n_t1 = Frame(frame_show1)
        frame_n_t1.pack(side='top')
        for one in range(len(u) - 1):
            frame_one = Frame(frame_n_t1)
            frame_one.pack(side='left')
            frame_nums = Frame(frame_one)
            frame_nums.pack(side='top')
            frame_t = Frame(frame_one)
            frame_t.pack(side='left')
            nu = [[Entry(frame_nums, width=6, justify='center', font=("宋体", 15)) for _ in range(len(d))] for _ in d]
            for row in range(len(nu)):
                for column in range(len(nu[0])):
                    if row == 0 and column == 0:
                        nu[row][column].insert('end', u[one + 1])
                    elif row == 0 and column != 0:
                        nu[row][column].insert('end', d[column])
                    elif row != 0 and column == 0:
                        nu[row][column].insert('end', d[row])
                    else:
                        numb = (number[h][row - 1][column - 1])
                        try:
                            nu[row][column].insert('end', str(s[numb]))
                        except:
                            nu[row][column].insert('end', str(number[h][row - 1][column - 1]))
                    nu[row][column].grid(row=row, column=column)
            h += 1
            if h == 1:
                global text0
                text0 = Text(frame_t, font=("宋体", 15), width=6 * (len(d)), height=2 * (len(d) + 1))
                text0.config(foreground='green')
                text0.pack(side='right')
            text = Text(frame_t, font=("宋体", 15), width=6 * (len(d)), height=2 * (len(d) + 1))
            number0 = [[float(eval(i.get())) for i in j[1:]] for j in nu[1:]]
            numbers.append(number0)
            tip0 = ['Weight:', 'A_w:', 'λ_max:', 'CI:', 'RI:', 'CR:', 'Best:', '一致性检验:']
            info0 = check(number0, method=method)
            if h == 1:
                text.insert('end', f'{names[0]}及一致性检验：' + '\n')
            else:
                text.insert('end', f'{names[h - 1]}及一致性检验：' + '\n')
            for i in range(8):
                if isinstance(info0[i], list):
                    text.insert('end', tip0[i] + str([round(j, round_) for j in info0[i]]) + '\n')
                else:
                    if tip0[i] == 'Best:':
                        text.insert('end', tip0[i] + str(d[int(info0[i]) + 1]) + '\n')
                    else:
                        text.insert('end', tip0[i] + str(info0[i]) + '\n')
            if not info0[-1]:
                text.config(foreground='red')
            text.pack()

    table1(t_, t_o)
    if len(o_w) > 1:
        table1(t_o, o_w, h=1)
    print()
    tip = ['W_0:', 'W_1:', 'W_2:', 'Best:', 'CR:', '整体一致性检验:']
    info = choice_best(numbers[0], numbers[1:], method)
    text0.insert('end', '最终结果及整体一致性检验：' + '\n')
    for i in range(6):
        if isinstance(info[i], list):
            if not isinstance(info[i][0], list):
                text0.insert('end', tip[i] + str([round(j, round_) for j in info[i]]) + '\n')
            else:
                text0.insert('end', tip[i] + str([[round(k, round_) for k in j] for j in info[i]]) + '\n')
        else:
            if tip[i] == 'Best:':
                text0.insert('end', tip[i] + str(o_w[int(info[i]) + 1]) + '\n')
            else:
                text0.insert('end', tip[i] + str(info[i]) + '\n')
    if not info[-1]:
        text0.config(foreground='red')
    text0.pack(side='right')


button0 = Button(frame_first, text="生成表格", command=first, font=('宋体', 15))
button0.pack(side='left')
frame_way = Frame(root)
# 选择方法
label3 = Label(frame_way, text='计算方法：', font=('宋体', 15))
label3.pack(side='left')
b = IntVar()
radio3_2 = Radiobutton(frame_way, text='方根法', font=('宋体', 15), variable=b, value=1)
radio3_2.pack(side='left')
radio3_1 = Radiobutton(frame_way, text='和积法', font=('宋体', 15), variable=b, value=0)
radio3_1.pack(side='left')
# radio3_0 = tk.Radiobutton(frame_way, text='尚未定义', font=('宋体', 15), variable=b, value=3)
# radio3_0.pack(side='left')
radio3_2.select()

# 小数
label0 = Label(frame_way, text='显示小数：', font=('宋体', 15))
label0.pack(side='left')
entry_small_num = Entry(frame_way, font=("宋体", 15), fg='blue')
entry_small_num.insert(0, '4')
entry_small_num.config(width=3, justify='center')
entry_small_num.pack(side='left')
label0 = Label(frame_way, text='位', font=('宋体', 15))
label0.pack(side='left')
Button(frame_way, text="点击求解", fg='red', command=solve, font=('宋体', 15)).pack(side='left')

root.mainloop()
# 什么叫屎山代码？真尼玛的服了，写出来全是屎，还一堆bug。
# 用就行了，没有解读意义。
